﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Reflection;

namespace EasyMvvm
{
    public sealed class ViewContainer : Control
    {
        Label noViewLoaded = new Label() { Content = "No view loaded!", VerticalAlignment=VerticalAlignment.Center, HorizontalAlignment=HorizontalAlignment.Center };
        static ViewContainer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ViewContainer), new FrameworkPropertyMetadata(typeof(ViewContainer)));
        }

        public ViewContainer()
        {
#if SILVERLIGHT
            Binding b = new Binding("DataContext");
            b.Mode = BindingMode.OneWay;
            b.Source = this;
            this.SetBinding(privateDataContextProperty, b);
            DataContextChanged += ViewContainer_DataContextChanged;
#else
            base.DataContextChanged += new System.Windows.DependencyPropertyChangedEventHandler(ViewContainer_DataContextChanged);
#endif
            View = noViewLoaded;
        }


        public FrameworkElement View
        {
            get { return (FrameworkElement)GetValue(ViewProperty); }
            set { SetValue(ViewProperty, value); }
        }

        public static readonly DependencyProperty ViewProperty =
            DependencyProperty.Register("View", typeof(FrameworkElement), typeof(ViewContainer), new PropertyMetadata(null));




#if SILVERLIGHT
        // Using a DependencyProperty as the backing store for privateDataContext.  This enables animation, styling, binding, etc...
            private static readonly DependencyProperty privateDataContextProperty =
                DependencyProperty.Register("privateDataContext", typeof(object), typeof(ViewContainer), new PropertyMetadata(new PropertyChangedCallback(PrivateDataContextChanged)));

        private static void PrivateDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ViewContainer fx = (ViewContainer)d;
            fx.DataContextChanged();
        }
#else
        void ViewContainer_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
        {
            DataContextChanged();
        }
#endif
        //Not implemented yet
        //private List<UIElement> viewCash = new List<UIElement>();

        public ViewModelMapping Mapping
        {
            get { return (ViewModelMapping)GetValue(MappingProperty); }
            set { SetValue(MappingProperty, value); }
        }

        public static readonly DependencyProperty MappingProperty =
            DependencyProperty.Register("Mapping", typeof(ViewModelMapping), typeof(ViewContainer), new UIPropertyMetadata(new PropertyChangedCallback(Mapping_PropertyChangedCallback)));

        private static void Mapping_PropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((ViewContainer)sender).OnMappingChanged();
        }

        void OnMappingChanged()
        {
            CreateNewView();
        }

        private new void DataContextChanged()
        {
            //Ensure that this method is run on the GUI Thread.
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(new Action(OnDataContextChanged));
            }
            else
            {
                OnDataContextChanged();
            }
        }

        void OnDataContextChanged()
        {
            CreateNewView();
        }

        void CreateNewView()
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(new Action(CreateNewView));
                return;
            }
            View = noViewLoaded;
            if (base.DataContext == null)
                return;

            ViewModelMapping mapping = Mapping;
            if (mapping == null)
                return;

            foreach (MappingEntry entry in mapping)
            {
                if (entry.ViewModelType == base.DataContext.GetType())
                {
                    FrameworkElement element = (FrameworkElement)Activator.CreateInstance(entry.ViewType);

                    MethodInfo initializeComponentinfo = entry.ViewType.GetMethod("InitializeComponent");
                    if (initializeComponentinfo != null)
                        initializeComponentinfo.Invoke(element, null);

                    if (element != null)
                    {
                        element.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                        element.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                        element.Margin = new Thickness(0);
                        View = element;
                    }
                    return;
                }
            }
        }
    }
}
